/*
 * Copyright (c) 2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
 * Redistribution and modifications are permitted subject to BSD license.
 */
#include <asn_internal.h>
#include <OPEN_TYPE.h>
#include <constr_CHOICE.h>
#include <errno.h>

asn_dec_rval_t OPEN_TYPE_oer_get(
    const asn_codec_ctx_t* opt_codec_ctx, const asn_TYPE_descriptor_t* td,
    void* sptr, asn_TYPE_member_t* elm, const void* ptr, size_t size) {
  asn_type_selector_result_t selected;
  void* memb_ptr;   /* Pointer to the member */
  void** memb_ptr2; /* Pointer to that pointer */
  void* inner_value;
  asn_dec_rval_t rv;
  size_t ot_ret;

  if (!(elm->flags & ATF_OPEN_TYPE)) {
    ASN__DECODE_FAILED;
  }

  if (!elm->type_selector) {
    ASN_DEBUG(
        "Type selector is not defined for Open Type %s->%s->%s", td->name,
        elm->name, elm->type->name);
    ASN__DECODE_FAILED;
  }

  selected = elm->type_selector(td, sptr);
  if (!selected.presence_index) {
    ASN__DECODE_FAILED;
  }

  /* Fetch the pointer to this member */
  if (elm->flags & ATF_POINTER) {
    memb_ptr2 = (void**) ((char*) sptr + elm->memb_offset);
  } else {
    memb_ptr  = (char*) sptr + elm->memb_offset;
    memb_ptr2 = &memb_ptr;
  }
  if (*memb_ptr2 != NULL) {
    /* Make sure we reset the structure first before encoding */
    if (CHOICE_variant_set_presence(elm->type, *memb_ptr2, 0) != 0) {
      ASN__DECODE_FAILED;
    }
  }

  inner_value = (char*) *memb_ptr2 +
                elm->type->elements[selected.presence_index - 1].memb_offset;

  ot_ret = oer_open_type_get(
      opt_codec_ctx, selected.type_descriptor, NULL, &inner_value, ptr, size);
  switch (ot_ret) {
    default:
      if (CHOICE_variant_set_presence(
              elm->type, *memb_ptr2, selected.presence_index) == 0) {
        rv.code     = RC_OK;
        rv.consumed = ot_ret;
        return rv;
      } else {
        /* Oh, now a full-blown failure failure */
      }
      /* Fall through */
    case -1:
      rv.code     = RC_FAIL;
      rv.consumed = ot_ret;
      break;
    case 0:
      rv.code     = RC_WMORE;
      rv.consumed = 0;
      break;
  }

  if (*memb_ptr2) {
    const asn_CHOICE_specifics_t* specs = selected.type_descriptor->specifics;
    if (elm->flags & ATF_POINTER) {
      ASN_STRUCT_FREE(*selected.type_descriptor, inner_value);
      *memb_ptr2 = NULL;
    } else {
      ASN_STRUCT_FREE_CONTENTS_ONLY(*selected.type_descriptor, inner_value);
      memset(*memb_ptr2, 0, specs->struct_size);
    }
  }
  return rv;
}
